Flutter http RetryClient simple example

Flutter HTTP RetryClient Example

One can only hope that developers who implement HTTP calls in their Flutter apps are aware of the `RetryClient` class. Although http examples provided by streamlined tutorials across the web are helpful, they may not always provide a complete picture of the `http` package’s capabilities. And because calls to common API endpoints are typically developed under happy path conditions, it can be easy to overlook the need for something like the RetryClient class.

[http: RetryClient] By default, this retries any request whose response has status code 503 Temporary Failure up to three retries. It waits 500ms before the first retry, and increases the delay by 1.5x each time. All of this can be customized using the RetryClient() constructor.

Google: pub.dev | http package

I was attempting to fix an uncaught exception error when loading an app on an emulator when I discovered the RetryClient class. And although it didn’t help fix that particular exception, it really felt this class should be a part of any HTTP call. Like, http and the RetryClient wrapper should be part and parcel to every HTTP call. Like selling hardware firewalls with computers. Like, a house with a roof. Or, you know, like, peanut butter and talking horses.

In any case, I couldn’t find many expanded examples for the RetryClient class in my search, so I took a little time to develop it out manually. The following is the result.

I hope it helps someone, and I’m always open to suggestions for making things better.

import 'dart:developer';

///
/// *** The CONFIGS ***
///

/// These HTTP response status codes determine if an HTTP call is retried below.
/// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
final httpRetryResponseStatusCodes = <int>[
  400, // | Bad Request
  401, // | Unauthorized
  403, // | Forbidden
  404, // | Not Found
  408, // | Request Timeout
  409, // | Conflict
  429, // | Too Many Requests
  500, // | Internal Server Error
  502, // | Bad Gateway
  503, // | Service Unavailable (Temporary Failure)
  504, // | Gateway Timeout
];

const apiUrl = 'https://yourdomain.com/api/endpoint.php';
final apiUri = Uri.parse(apiUrl);

const apiErr = 'Retry error: Please check your connection and try again.';

const whichForm = 'login';
final returnObj = <String, List<dynamic>>{
  'httpStatus': [],
  'status': [],
  'message': [],
  'whichForm': [whichForm],
};

final (userId, token) = await _injectedNetworkServiceApi.getAuthInfo;
final urlData = {
  'userId': userId,
  'userToken': token,
  'whichForm': whichForm,
};

late final Response response;

///
/// *** The RETRY ***
///

/// RetryClient Sources:
///   https://pub.dev/packages/http#retrying-requests
///   https://pub.dev/documentation/http/latest/retry/RetryClient-class.html
///
final client = RetryClient(
  http.Client(),
  // retries: 7, // Default: 3
  whenError: (error, stackTrace) => true,
  when: (response) => httpRetryResponseStatusCodes.contains(response.statusCode),
  onRetry: (request, response, retryCount) {
    log('[RetryClient] [onRetry] [$retryCount] $request');
  },
);

try {
  response = await client.post(
    apiUri,
    body: json.encode(urlData),
  );
} catch (err) {
  returnObj['httpStatus']!.add('500');
  returnObj['status']!.add('error');
  returnObj['message']!.add(apiErr);
  return returnObj;
  //
} finally {
  client.close();
}

final responseData = json.decode(response.body) as Map<String, dynamic>;

Cheers!
– Keith | https://keithdc.com

Leave a Reply